﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FyLog;
using System.Threading;
using System.IO;
using System.Diagnostics;

/// <summary>
/// 日志工具单文件版
/// 2018-05-14 v1.0.1 zzj
/// 2021-04-14 v1.0.2 zzj 修复日志量比较小，当Log.txt文件超过了日志有效期后被删除，导致后续写日志出错的bug
/// </summary>

public class FyLogContext
{
    private static Queue<LogItem> _queLogs = new Queue<LogItem>();      //日志队列

    private static double _singleFileSize = 10 * 1024 * 1024;           //默认10M
    private static bool _showToConsole = false;                         //默认不显示到console
    private static string _path = AppDomain.CurrentDomain.BaseDirectory + "Logs\\";     //默认日志路径
    private static FileStream _fs;

    /// <summary>
    /// 日志保留日期，如果为0，则永久保留
    /// </summary>
    public static int ExpiredDate { get; set; }

    /// <summary>
    /// 初始化日志
    /// </summary>
    public static void InitLog()
    {
        CreateThreading();
    }

    /// <summary>
    /// 初始化日志
    /// </summary>
    /// <param name="showToConsole"></param>
    /// <param name="path"></param>
    public static void InitLog(bool showToConsole, string path)
    {
        _showToConsole = showToConsole;
        _path = path;
        CreateThreading();
    }

    /// <summary>
    /// 初始化日志
    /// </summary>
    /// <param name="path"></param>
    public static void InitLog(string path)
    {
        _path = path;
        CreateThreading();
    }

    /// <summary>
    /// 初始化日志
    /// </summary>
    /// <param name="singleFileSize">单文件大小(M)</param>
    /// <param name="showToConsole">是否显示到Console</param>
    /// <param name="path">日志文件名</param>
    public static void InitLog(double singleFileSize, bool showToConsole, string path)
    {
        if (singleFileSize > 0)
            _singleFileSize = singleFileSize * 1024 * 1024;
        _showToConsole = showToConsole;
        _path = path;
        CreateThreading();
    }

    /// <summary>
    /// 写日志
    /// </summary>
    /// <param name="log"></param>
    public static void Write(string log)
    {
        try
        {
            LogItem item = new LogItem(_showToConsole, log, DateTime.Now);
            AddToQueue(item);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    /// <summary>
    /// 写日志
    /// </summary>
    /// <param name="log"></param>
    /// <param name="showToConsole"></param>
    public static void Write(string log, bool showToConsole)
    {
        try
        {
            LogItem item = new LogItem(showToConsole, log, DateTime.Now);
            AddToQueue(item);
        }
        catch (Exception)
        {
            throw;
        }
    }

    /// <summary>
    /// 写日志
    /// </summary>
    public static void Write(Exception log)
    {
        try
        {
            LogItem item = new FyLog.LogItem(_showToConsole, log, DateTime.Now);
            AddToQueue(item);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    /// <summary>
    /// 写日志
    /// </summary>
    public static void Write(Exception log, bool showToConsole)
    {
        try
        {
            LogItem item = new FyLog.LogItem(showToConsole, log, DateTime.Now);
            AddToQueue(item);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    /// <summary>
    /// 设置是否显示到console
    /// </summary>
    /// <param name="showToConsole"></param>
    public void SetShowToConsole(bool showToConsole)
    {
        _showToConsole = showToConsole;
    }

    /// <summary>
    /// 创建线程
    /// </summary>
    private static void CreateThreading()
    {
        try
        {
            Task tsk = new Task(new Action(() =>
            {
                try
                {
                    if (Directory.Exists(_path) == false)
                        Directory.CreateDirectory(_path);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                }

                while (true)
                {
                    WriteToFile();
                }
            }));
            tsk.Start();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    /// <summary>
    /// 添加到队列
    /// </summary>
    /// <param name="logItem"></param>
    private static void AddToQueue(LogItem logItem)
    {
        try
        {
            lock (_queLogs)
            {
                _queLogs.Enqueue(logItem);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }

    /// <summary>
    /// 从队列中获取数据
    /// </summary>
    /// <returns></returns>
    private static List<LogItem> DeQueue()
    {
        List<LogItem> res = null;
        try
        {
            lock (_queLogs)
            {
                int cnt = _queLogs.Count;
                if (cnt > 0)
                {
                    res = new List<FyLog.LogItem>();
                    for (int i = 0; i < cnt; i++)
                    {
                        res.Add(_queLogs.Dequeue());
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        return res;
    }

    /// <summary>
    /// 写入文件，线程
    /// </summary>
    private static void WriteToFile()
    {
        while (true)
        {
            try
            {
                List<LogItem> logs = DeQueue();
                if (logs == null)
                {
                    Thread.Sleep(1000);
                    continue;
                }

                if (_fs == null)
                    _fs = CreateFileStream();
                else
                {
                    if (_fs.Length >= _singleFileSize)
                    {
                        _fs.Close();
                        _fs.Dispose();
                        _fs = CreateFileStream();
                    }
                }

                foreach (LogItem log in logs)
                {
                    if (log.ShowToConsole)
                        Console.WriteLine(log.Log);

                    byte[] byteData = Encoding.UTF8.GetBytes(log.Log + "\n");
                    _fs.Write(byteData, 0, byteData.Length);
                    _fs.Flush();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    /// <summary>
    /// 创建文件写入流
    /// </summary>
    /// <returns></returns>
    private static FileStream CreateFileStream()
    {
        FileStream fs = null;
        try
        {
            string fileName = _path + "Log.txt";
            FileInfo fi = new FileInfo(fileName);
            if (fi.Exists)
            {
                if (fi.Length >= _singleFileSize)
                {
                    if (ExpiredDate > 0)
                    {
                        //如果用户设定了日志保留日期,则删除过期日志
                        DirectoryInfo di = new DirectoryInfo(_path);
                        FileInfo[] files = di.GetFiles();
                        if (files != null)
                        {
                            foreach (FileInfo fInfo in files)
                            {
                                if ((DateTime.Now - fInfo.CreationTime).TotalDays > ExpiredDate && fInfo.Name != "Log.txt")
                                    fInfo.Delete();
                            }
                        }
                    }

                    fi.MoveTo(_path + "Log_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt");
                    fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read);
                }
                else
                    fs = new FileStream(fileName, FileMode.Append, FileAccess.Write, FileShare.Read);
            }
            else
                fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read);
        }
        catch (Exception)
        {
            throw;
        }
        return fs;
    }
}

namespace FyLog
{
    public class LogItem
    {
        public bool ShowToConsole { get; set; }
        public string Log { get; set; }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="logType"></param>
        /// <param name="showToConsole"></param>
        /// <param name="log"></param>
        public LogItem(bool showToConsole, string log, DateTime logTime)
        {
            try
            {
                ShowToConsole = showToConsole;

                //生成日志文本
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("{0} {1}", logTime.ToString("yyyy-MM-dd HH:mm:ss"), log);
                Log = sb.ToString();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="showToConsole"></param>
        /// <param name="log"></param>
        /// <param name="logTime"></param>
        public LogItem(bool showToConsole, Exception log, DateTime logTime)
        {
            try
            {
                ShowToConsole = showToConsole;

                //生成日志文本
                StringBuilder sb = new StringBuilder();
                sb.AppendFormat("{0} 错误:{1},{2}\n{3}", logTime.ToString("yyyy-MM-dd HH:mm:ss"), log.ToString(), log.Message, log.StackTrace);
                Log = sb.ToString();
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}